/**
 * @file   typelist.hpp
 * @author wolfhead <wolfhead@wolfhead-laptop>
 * @date   Wed Sep 21 12:56:00 2011
 * 
 * @brief  
 * 
 * 
 */
#ifndef ACAPELLA_TYPELIST_TYPELIST_HPP
#define ACAPELLA_TYPELIST_TYPELIST_HPP

#include "typelist_macro.hpp"
#include "../tools.hpp"

namespace acapella
{

struct NullType {};

struct EmptyType {};

template <typename T, typename U> 
struct Typelist
{
    typedef T Head;
    typedef U Tail;
};


namespace TL
{

//////////////////////////////////////////////////////////////////////
// Length
/**
 * Lenght get the length of a Typelist
 * 
 */
template <typename TList>
struct Length;

template <>
struct Length<NullType>
{
    enum {value = 0};
};

template <typename T, typename U>
struct Length<Typelist<T, U> >
{
    enum {value = 1 + Length<U>::value};
};

//////////////////////////////////////////////////////////////////////
// TypeAt
/**
 * TypeAt access the type at the given index
 * 
 */
template <typename TList, unsigned int index>
struct TypeAt;

template <typename Head, typename Tail>
struct TypeAt<Typelist<Head, Tail>, 0>
{
    typedef Head Result;
};

template <typename Head, typename Tail, unsigned int index>
struct TypeAt<Typelist<Head, Tail>, index>
{
    typedef typename TypeAt<Tail, index - 1>::Result Result;
};

//////////////////////////////////////////////////////////////////////
// TypeAtNonStrict

template <typename TList, unsigned int index, typename DefaultType = NullType>

struct TypeAtNonStrict;

template <unsigned int index, typename DefaultType>
struct TypeAtNonStrict<NullType, index, DefaultType>
{
    typedef DefaultType Result;
};

template <typename Head, typename Tail, typename DefaultType>
struct TypeAtNonStrict<Typelist<Head, Tail>, 0, DefaultType>
{
    typedef Head Result;
};

template <
    typename Head, 
    typename Tail, 
    unsigned int index, 
    typename DefaultType
    >
struct TypeAtNonStrict<Typelist<Head, Tail>, index, DefaultType>
{
    typedef typename TypeAtNonStrict<Tail, index - 1, DefaultType>::Result Result;
};


//////////////////////////////////////////////////////////////////////
// IndexOf<TList, T>
// if (TList is NullType) then value = -1
// else if (TList is a TypeList with T as its Head) then
//     value = 0
// else if (TList as a TypeList) then
//     value = 1 + IndexOF<TList::Tail, T>

template<typename TList, typename T>
struct IndexOf;

template<typename T>
struct IndexOf<NullType, T>
{
    enum {value = -1};
};

template<typename Tail, typename T>
struct IndexOf<Typelist<T, Tail>, T>
{
    enum {value = 0};
};

template<typename Head, typename Tail, typename T>
struct IndexOf<Typelist<Head, Tail>, T>
{
private:
    enum {temp = IndexOf<Tail, T>::value};
public:
    enum {value = temp == -1 ? -1 : temp + 1};
};

//////////////////////////////////////////////////////////////////////
// Append<TList, T>
// Append T at the end if TList
//
// if (TList is NullType and T is NullType) Result is NullType
// else 
//    if (TList is NullType and T is a single type) then 
//       Result is a TypeList with a only element T
//    else if (TList is NullType and T is a TypeList) then
//       Result is a TypeList
//    else if (TList is TypeList) then
//       Result is a TypeList having TList::Head as its Head 
//       and append T to TList::Tail as its Tail 

template<typename TList, typename T>
struct Append;

template<>
struct Append<NullType, NullType>
{
    typedef NullType Result;
};

template<typename T>
struct Append<NullType, T>
{
    typedef TYPELIST_1(T) Result;
};

template<typename Head, typename Tail>
struct Append<NullType, Typelist<Head, Tail> >
{
    typedef Typelist<Head, Tail> Result;
};

template<typename Head, typename Tail, typename T>
struct Append<Typelist<Head, Tail>, T>
{
    typedef Typelist<Head, 
                     typename Append<Tail, T>::Result>
    Result;
};

//////////////////////////////////////////////////////////////////////
// Erase<TList, T>
// Erase the first occrance of T in TList
//
// if (TList is NullType) then Result is NullType
// else if (TList is a TypeList with T as its head) then
//     Result is TList::Tail
// else if (TList is a TypeList) then
//     Result is a TypeLsit with TList::Head as its head
//     and Erase<TList::Tail, T> as its Tail
//

template <typename TList, typename T>
struct Erase;

template <typename T>
struct Erase<NullType, T>
{
    typedef NullType Result;
};

template <typename Tail, typename T>
struct Erase<Typelist<T, Tail>, T>
{
    typedef Tail Result;
};

template <typename Head, typename Tail, typename T>
struct Erase<Typelist<Head, Tail>, T>
{
    typedef Typelist<Head,
                     typename Erase<Tail, T>::Result>
    Result;
};


//////////////////////////////////////////////////////////////////////
// EraseAll<TList, T>
// EraseAll the first occrance of T in TList
//
// if (TList is NullType) then Result is NullType
// else if (TList is a TypeList with T as its head) then
//     Result is EraseAll<TList::Tail, T>
// else if (TList is a TypeList) then
//     Result is a TypeLsit with TList::Head as its head
//     and EraseAll<TList::Tail, T> as its Tail
//

template <typename TList, typename T>
struct EraseAll;

template <typename T>
struct EraseAll<NullType, T>
{
    typedef NullType Result;
};

template <typename Tail, typename T>
struct EraseAll<Typelist<T, Tail>, T>
{
    typedef typename EraseAll<Tail, T>::Result Result;
};

template <typename Head, typename Tail, typename T>
struct EraseAll<Typelist<Head, Tail>, T>
{
    typedef Typelist<Head,
                     typename EraseAll<Tail, T>::Result>
    Result;
};

//////////////////////////////////////////////////////////////////////
// NoDuplicates<TList>
// NoDuplicates remove duplicate values in TList
// 
// if (TList is NullType) then Result = NullType
// else 
//    Apply NoDuplicate to TList::Tail obtain L1
//    Apply Erase TList::Head to L1 obtain L2
//    Result is a TypeList whose Head is TList::Head 
//    and whose Tail is L2
//

template<typename T>
struct NoDuplicate;

template <>
struct NoDuplicate<NullType>
{
    typedef NullType Result;
};

template <typename Head, typename Tail>
struct NoDuplicate<Typelist<Head, Tail> >
{
private:
    typedef typename NoDuplicate<Tail>::Result L1;
    typedef typename Erase<L1, Head>::Result L2;
public:
    typedef Typelist<Head, L2> Result;
};

//////////////////////////////////////////////////////////////////////
// Replace<TList, T, U>
// Replace the first occurance if T in TList as U
//
// if (TList is NullType) Result is NullType
// else if (TList is a Typelist with T as its head) then
//    Result is a TypeList with U as its Head TList::Tail as its Tail
// else if (TList is a TypeList) then
//    Result is a TypeList with TList::Head as its head
//    Replace<TList::Tail, T, U>::Result as its Tail
//

template <typename TList, typename T, typename U>
struct Replace;

template <typename T, typename U>
struct Replace<NullType, T, U>
{
    typedef NullType Result;
};

template <typename Tail, typename T, typename U>
struct Replace<Typelist<T, Tail>, T, U>
{
    typedef Typelist<U, Tail> Result;
};

template <typename Head, typename Tail, typename T, typename U>
struct Replace<Typelist<Head, Tail>, T, U>
{
    typedef Typelist<Head,
                     typename Replace<Tail, T, U>::Result>
    Result;
};


//////////////////////////////////////////////////////////////////////
// ReplaceAll<TList, T, U>
// ReplaceAll all occurance if T in TList as U
//
// if (TList is NullType) Result is NullType
// else if (TList is a Typelist with T as its head) then
//    Result is a TypeList with U as its Head 
//    ReplaceAll<TList::Tail, T, U>::Result as its Tail
// else if (TList is a TypeList) then
//    Result is a TypeList with TList::Head as its head
//    ReplaceAll<TList::Tail, T, U>::Result as its Tail
//

template <typename TList, typename T, typename U>
struct ReplaceAll;

template <typename T, typename U>
struct ReplaceAll<NullType, T, U>
{
    typedef NullType Result;
};

template <typename Tail, typename T, typename U>
struct ReplaceAll<Typelist<T, Tail>, T, U>
{
    typedef Typelist<U, 
                     typename ReplaceAll<Tail, T, U>::Result> 
        Result;
};

template <typename Head, typename Tail, typename T, typename U>
struct ReplaceAll<Typelist<Head, Tail>, T, U>
{
    typedef Typelist<Head,
                     typename ReplaceAll<Tail, T, U>::Result>
    Result;
};

//////////////////////////////////////////////////////////////////////
// MostDerived<TList, T>
// MostDerived Get the most derived type of T in TList
//

template <typename TList, typename T>
struct MostDerived;

template <typename T>
struct MostDerived<NullType, T>
{
    typedef T Result;
};

template <typename Head, typename Tail, typename T>
struct MostDerived<Typelist<Head, Tail>, T>
{
private:
    typedef typename MostDerived<Tail, T>::Result Candidate;
public:
    typedef typename Select<
    SUPERSUBCLASS(Candidate, Head),
    Head, Candidate>::type Result;
};

//////////////////////////////////////////////////////////////////////
// DerivedToFront
//

template <typename TList>
struct DerivedToFront;

template <>
struct DerivedToFront<NullType>
{
    typedef NullType Result;
};


template <typename Head, typename Tail>
struct DerivedToFront<Typelist<Head, Tail> >
{
private:
    typedef typename MostDerived<Tail, Head>::Result TheMostDerived;
    typedef typename Replace<Tail, TheMostDerived, Head>::Result L;
public:
    typedef Typelist<TheMostDerived, L> Result;
};


}//namepspace TL
}//namespace acapella

#endif
